Completed
Push — master ( 3347f9...51b18f )
by Ruben de
02:24
created

APIClient.transaction   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
dl 0
loc 5
rs 9.4285
nop 2
1
var _ = require('lodash'),
2
    q = require('q'),
3
    bitcoin = require('bitcoinjs-lib'),
4
    bitcoinMessage = require('bitcoinjs-message'),
5
6
    bip39 = require("bip39"),
7
    Wallet = require('./wallet'),
8
    RestClient = require('./rest_client'),
9
    Encryption = require('./encryption'),
10
    KeyDerivation = require('./keyderivation'),
11
    EncryptionMnemonic = require('./encryption_mnemonic'),
12
    blocktrail = require('./blocktrail'),
13
    randomBytes = require('randombytes'),
14
    CryptoJS = require('crypto-js'),
15
    webworkifier = require('./webworkifier');
16
17
var useWebWorker = require('./use-webworker')();
18
19
/**
20
 * Bindings to conssume the BlockTrail API
21
 *
22
 * @param options       object{
23
 *                          apiKey: 'API_KEY',
24
 *                          apiSecret: 'API_SECRET',
25
 *                          host: 'defaults to api.blocktrail.com',
26
 *                          network: 'BTC|LTC',
27
 *                          testnet: true|false
28
 *                      }
29
 * @constructor
30
 */
31
var APIClient = function(options) {
32
    var self = this;
33
34
    // handle constructor call without 'new'
35
    if (!(this instanceof APIClient)) {
36
        return new APIClient(options);
37
    }
38
39
    self.testnet = options.testnet = options.testnet || false;
40
    if (self.testnet) {
41
        self.network = bitcoin.networks.testnet;
42
    } else {
43
        self.network = bitcoin.networks.bitcoin;
44
    }
45
46
    self.bitcoinCash = options.network && options.network === "BCC";
47
48
    options.apiNetwork = options.apiNetwork || ((self.testnet ? "t" : "") + (options.network || 'BTC').toUpperCase());
49
50
    /**
51
     * @type RestClient
52
     */
53
    self.client = APIClient.initRestClient(options);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
54
};
55
56
APIClient.initRestClient = function(options) {
57
    // BLOCKTRAIL_SDK_API_ENDPOINT overwrite for development
58
    if (process.env.BLOCKTRAIL_SDK_API_ENDPOINT) {
59
        options.host = process.env.BLOCKTRAIL_SDK_API_ENDPOINT;
60
    }
61
62
    // trim off leading https?://
63
    if (options.host && options.host.indexOf("https://") === 0) {
64
        options.https = true;
65
        options.host = options.host.substr(8);
66
    } else if (options.host && options.host.indexOf("http://") === 0) {
67
        options.https = false;
68
        options.host = options.host.substr(7);
69
    }
70
71
    if (typeof options.https === "undefined") {
72
        options.https = true;
73
    }
74
75
    if (!options.host) {
76
        options.host = 'api.blocktrail.com';
77
    }
78
79
    if (!options.port) {
80
        options.port = options.https ? 443 : 80;
81
    }
82
83
    if (!options.endpoint) {
84
        options.endpoint = "/" + (options.apiVersion || "v1") + (options.apiNetwork ? ("/" + options.apiNetwork) : "");
85
    }
86
87
    return new RestClient(options);
88
};
89
90
var determineDataStorageV2_3 = function(options) {
91
    return q.when(options)
92
        .then(function(options) {
93
            // legacy
94
            if (options.storePrimaryMnemonic) {
95
                options.storeDataOnServer = options.storePrimaryMnemonic;
96
            }
97
98
            // storeDataOnServer=false when primarySeed is provided
99
            if (typeof options.storeDataOnServer === "undefined") {
100
                options.storeDataOnServer = !options.primarySeed;
101
            }
102
103
            return options;
104
        });
105
};
106
107
var produceEncryptedDataV2 = function(options, notify) {
108
    return q.when(options)
109
        .then(function(options) {
110
            if (options.storeDataOnServer) {
111
                if (!options.secret) {
112
                    if (!options.passphrase) {
113
                        throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
114
                    }
115
116
                    notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
117
118
                    options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
119
                    options.encryptedSecret = CryptoJS.AES.encrypt(options.secret, options.passphrase).toString(CryptoJS.format.OpenSSL); // 'base64' string
120
                }
121
122
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
123
124
                options.encryptedPrimarySeed = CryptoJS.AES.encrypt(options.primarySeed.toString('base64'), options.secret)
125
                    .toString(CryptoJS.format.OpenSSL); // 'base64' string
126
                options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
127
128
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
129
130
                options.recoveryEncryptedSecret = CryptoJS.AES.encrypt(options.secret, options.recoverySecret)
131
                                                              .toString(CryptoJS.format.OpenSSL); // 'base64' string
132
            }
133
134
            return options;
135
        });
136
};
137
138
APIClient.prototype.promisedEncrypt = function(pt, pw, iter) {
139
    if (useWebWorker) {
140
        // generate randomness outside of webworker because many browsers don't have crypto.getRandomValues inside webworkers
141
        var saltBuf = Encryption.generateSalt();
142
        var iv = Encryption.generateIV();
143
144
        return webworkifier.workify(APIClient.prototype.promisedEncrypt, function() {
145
            return require('./webworker');
146
        }, {
147
            method: 'Encryption.encryptWithSaltAndIV',
148
            pt: pt,
149
            pw: pw,
150
            saltBuf: saltBuf,
151
            iv: iv,
152
            iterations: iter
153
        })
154
            .then(function(data) {
155
                return Buffer.from(data.cipherText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
156
            });
157
    } else {
158
        try {
159
            return q.when(Encryption.encrypt(pt, pw, iter));
160
        } catch (e) {
161
            return q.reject(e);
162
        }
163
    }
164
};
165
166
APIClient.prototype.promisedDecrypt = function(ct, pw) {
167
    if (useWebWorker) {
168
        return webworkifier.workify(APIClient.prototype.promisedDecrypt, function() {
169
            return require('./webworker');
170
        }, {
171
            method: 'Encryption.decrypt',
172
            ct: ct,
173
            pw: pw
174
        })
175
            .then(function(data) {
176
                return Buffer.from(data.plainText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
177
            });
178
    } else {
179
        try {
180
            return q.when(Encryption.decrypt(ct, pw));
181
        } catch (e) {
182
            return q.reject(e);
183
        }
184
    }
185
};
186
187
APIClient.prototype.produceEncryptedDataV3 = function(options, notify) {
188
    var self = this;
189
190
    return q.when(options)
191
        .then(function(options) {
192
            if (options.storeDataOnServer) {
193
                return q.when()
194
                    .then(function() {
195
                        if (!options.secret) {
196
                            if (!options.passphrase) {
197
                                throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
198
                            }
199
200
                            notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
201
202
                            // -> now a buffer
203
                            options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
204
205
                            // -> now a buffer
206
                            return self.promisedEncrypt(options.secret, new Buffer(options.passphrase), KeyDerivation.defaultIterations)
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
207
                                .then(function(encryptedSecret) {
208
                                    options.encryptedSecret = encryptedSecret;
209
                                });
210
                        } else {
211
                            if (!(options.secret instanceof Buffer)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !(options.secret instanceof Buffer) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
212
                                throw new Error('Secret must be a buffer');
213
                            }
214
                        }
215
                    })
216
                    .then(function() {
217
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
218
219
                        return self.promisedEncrypt(options.primarySeed, options.secret, KeyDerivation.subkeyIterations)
220
                            .then(function(encryptedPrimarySeed) {
221
                                options.encryptedPrimarySeed = encryptedPrimarySeed;
222
                            });
223
                    })
224
                    .then(function() {
225
                        // skip generating recovery secret when explicitly set to false
226
                        if (options.recoverySecret === false) {
227
                            return;
228
                        }
229
230
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
231
                        if (!options.recoverySecret) {
232
                            options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
233
                        }
234
235
                        return self.promisedEncrypt(options.secret, options.recoverySecret, KeyDerivation.defaultIterations)
236
                            .then(function(recoveryEncryptedSecret) {
237
                                options.recoveryEncryptedSecret = recoveryEncryptedSecret;
238
                            });
239
                    })
240
                    .then(function() {
241
                        return options;
242
                    });
243
            } else {
244
                return options;
245
            }
246
        });
247
};
248
249
var doRemainingWalletDataV2_3 = function(options, network, notify) {
250
    return q.when(options)
251
        .then(function(options) {
252
            if (!options.backupPublicKey) {
253
                options.backupSeed = options.backupSeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
254
            }
255
256
            notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
257
258
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
259
260
            notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
261
262
            if (!options.backupPublicKey) {
263
                options.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.backupSeed, network);
264
                options.backupPublicKey = options.backupPrivateKey.neutered();
265
            }
266
267
            options.primaryPublicKey = options.primaryPrivateKey.deriveHardened(options.keyIndex).neutered();
268
269
            notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
270
271
            return options;
272
        });
273
};
274
275
APIClient.prototype.mnemonicToPrivateKey = function(mnemonic, passphrase, cb) {
276
    var self = this;
277
278
    var deferred = q.defer();
279
    deferred.promise.spreadNodeify(cb);
280
281
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
282
283
    deferred.resolve(q.fcall(function() {
284
        return self.mnemonicToSeedHex(mnemonic, passphrase).then(function(seedHex) {
285
            return bitcoin.HDNode.fromSeedHex(seedHex, network);
286
        });
287
    }));
288
289
    return deferred.promise;
290
};
291
292
APIClient.prototype.mnemonicToSeedHex = function(mnemonic, passphrase) {
293
    var self = this;
294
295
    if (useWebWorker) {
296
        return webworkifier.workify(self.mnemonicToSeedHex, function() {
297
            return require('./webworker');
298
        }, {method: 'mnemonicToSeedHex', mnemonic: mnemonic, passphrase: passphrase})
299
            .then(function(data) {
300
                return data.seed;
301
            });
302
    } else {
303
        try {
304
            return q.when(bip39.mnemonicToSeedHex(mnemonic, passphrase));
305
        } catch (e) {
306
            return q.reject(e);
307
        }
308
    }
309
};
310
311
APIClient.prototype.resolvePrimaryPrivateKeyFromOptions = function(options, cb) {
312
    var self = this;
313
314
    var deferred = q.defer();
315
    deferred.promise.nodeify(cb);
316
317
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
318
319
    try {
320
        // avoid conflicting options
321
        if (options.passphrase && options.password) {
322
            throw new blocktrail.WalletCreateError("Can't specify passphrase and password");
323
        }
324
        // normalize passphrase/password
325
        options.passphrase = options.passphrase || options.password;
326
        delete options.password;
327
328
        // avoid conflicting options
329
        if (options.primaryMnemonic && options.primarySeed) {
330
            throw new blocktrail.WalletInitError("Can only specify one of; Primary Mnemonic or Primary Seed");
331
        }
332
333
        // avoid deprecated options
334
        if (options.primaryPrivateKey) {
335
            throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
336
        }
337
338
        // make sure we have at least one thing to use
339
        if (!options.primaryMnemonic && !options.primarySeed) {
340
            throw new blocktrail.WalletInitError("Need to specify at least one of; Primary Mnemonic or Primary Seed");
341
        }
342
343
        if (options.primarySeed) {
344
            self.primarySeed = options.primarySeed;
345
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(self.primarySeed, network);
346
            deferred.resolve(options);
347
        } else {
348
            if (!options.passphrase) {
349
                throw new blocktrail.WalletInitError("Can't init wallet with Primary Mnemonic without a passphrase");
350
            }
351
352
            self.mnemonicToSeedHex(options.primaryMnemonic, options.passphrase)
353
                .then(function(seedHex) {
354
                    try {
355
                        options.primarySeed = new Buffer(seedHex, 'hex');
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
356
                        options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
357
                        deferred.resolve(options);
358
                    } catch (e) {
359
                        deferred.reject(e);
360
                    }
361
                }, function(e) {
362
                    deferred.reject(e);
363
                });
364
        }
365
    } catch (e) {
366
        deferred.reject(e);
367
    }
368
369
    return deferred.promise;
370
};
371
372
APIClient.prototype.resolveBackupPublicKeyFromOptions = function(options, cb) {
373
    var self = this;
374
375
    var deferred = q.defer();
376
    deferred.promise.nodeify(cb);
377
378
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
379
380
    try {
381
        // avoid conflicting options
382
        if (options.backupMnemonic && options.backupPublicKey) {
383
            throw new blocktrail.WalletInitError("Can only specify one of; Backup Mnemonic or Backup PublicKey");
384
        }
385
386
        // make sure we have at least one thing to use
387
        if (!options.backupMnemonic && !options.backupPublicKey) {
388
            throw new blocktrail.WalletInitError("Need to specify at least one of; Backup Mnemonic or Backup PublicKey");
389
        }
390
391
        if (options.backupPublicKey) {
392
            if (options.backupPublicKey instanceof bitcoin.HDNode) {
393
                deferred.resolve(options);
394
            } else {
395
                options.backupPublicKey = bitcoin.HDNode.fromBase58(options.backupPublicKey, network);
396
                deferred.resolve(options);
397
            }
398
        } else {
399
            self.mnemonicToPrivateKey(options.backupMnemonic, "").then(function(backupPrivateKey) {
400
                options.backupPublicKey = backupPrivateKey.neutered();
401
                deferred.resolve(options);
402
            }, function(e) {
403
                deferred.reject(e);
404
            });
405
        }
406
    } catch (e) {
407
        deferred.reject(e);
408
    }
409
410
    return deferred.promise;
411
};
412
413
APIClient.prototype.debugAuth = function(cb) {
414
    var self = this;
415
416
    return self.client.get("/debug/http-signature", null, true, cb);
417
};
418
419
/**
420
 * get a single address
421
 *
422
 * @param address      string       address hash
423
 * @param [cb]          function    callback function to call when request is complete
424
 * @return q.Promise
425
 */
426
APIClient.prototype.address = function(address, cb) {
427
    var self = this;
428
429
    return self.client.get("/address/" + address, null, cb);
430
};
431
432
APIClient.prototype.addresses = function(addresses, cb) {
433
    var self = this;
434
435
    return self.client.post("/address", null, {"addresses": addresses}, cb);
436
};
437
438
/**
439
 * get all transactions for an address (paginated)
440
 *
441
 * @param address       string      address hash
442
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
443
 * @param [cb]          function    callback function to call when request is complete
444
 * @return q.Promise
445
 */
446
APIClient.prototype.addressTransactions = function(address, params, cb) {
447
    var self = this;
448
449
    if (typeof params === "function") {
450
        cb = params;
451
        params = null;
452
    }
453
454
    return self.client.get("/address/" + address + "/transactions", params, cb);
455
};
456
457
/**
458
 * get all transactions for a batch of addresses (paginated)
459
 *
460
 * @param addresses     array       address hashes
461
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
462
 * @param [cb]          function    callback function to call when request is complete
463
 * @return q.Promise
464
 */
465
APIClient.prototype.batchAddressHasTransactions = function(addresses, params, cb) {
466
    var self = this;
467
468
    if (typeof params === "function") {
469
        cb = params;
470
        params = null;
471
    }
472
473
    return self.client.post("/address/has-transactions", params, {"addresses": addresses}, cb);
474
};
475
476
/**
477
 * get all unconfirmed transactions for an address (paginated)
478
 *
479
 * @param address       string      address hash
480
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
481
 * @param [cb]          function    callback function to call when request is complete
482
 * @return q.Promise
483
 */
484
APIClient.prototype.addressUnconfirmedTransactions = function(address, params, cb) {
485
    var self = this;
486
487
    if (typeof params === "function") {
488
        cb = params;
489
        params = null;
490
    }
491
492
    return self.client.get("/address/" + address + "/unconfirmed-transactions", params, cb);
493
};
494
495
/**
496
 * get all unspent outputs for an address (paginated)
497
 *
498
 * @param address       string      address hash
499
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
500
 * @param [cb]          function    callback function to call when request is complete
501
 * @return q.Promise
502
 */
503
APIClient.prototype.addressUnspentOutputs = function(address, params, cb) {
504
    var self = this;
505
506
    if (typeof params === "function") {
507
        cb = params;
508
        params = null;
509
    }
510
511
    return self.client.get("/address/" + address + "/unspent-outputs", params, cb);
512
};
513
514
/**
515
 * get all unspent outputs for a batch of addresses (paginated)
516
 *
517
 * @param addresses     array       address hashes
518
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
519
 * @param [cb]          function    callback function to call when request is complete
520
 * @return q.Promise
521
 */
522
APIClient.prototype.batchAddressUnspentOutputs = function(addresses, params, cb) {
523
    var self = this;
524
525
    if (typeof params === "function") {
526
        cb = params;
527
        params = null;
528
    }
529
530
    return self.client.post("/address/unspent-outputs", params, {"addresses": addresses}, cb);
531
};
532
533
/**
534
 * verify ownership of an address
535
 *
536
 * @param address       string      address hash
537
 * @param signature     string      a signed message (the address hash) using the private key of the address
538
 * @param [cb]          function    callback function to call when request is complete
539
 * @return q.Promise
540
 */
541
APIClient.prototype.verifyAddress = function(address, signature, cb) {
542
    var self = this;
543
544
    return self.client.post("/address/" + address + "/verify", null, {signature: signature}, cb);
545
};
546
547
/**
548
 * get all blocks (paginated)
549
 *
550
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
551
 * @param [cb]          function    callback function to call when request is complete
552
 * @return q.Promise
553
 */
554
APIClient.prototype.allBlocks = function(params, cb) {
555
    var self = this;
556
557
    if (typeof params === "function") {
558
        cb = params;
559
        params = null;
560
    }
561
562
    return self.client.get("/all-blocks", params, cb);
563
};
564
565
/**
566
 * get a block
567
 *
568
 * @param block         string|int  a block hash or a block height
569
 * @param [cb]          function    callback function to call when request is complete
570
 * @return q.Promise
571
 */
572
APIClient.prototype.block = function(block, cb) {
573
    var self = this;
574
575
    return self.client.get("/block/" + block, null, cb);
576
};
577
578
/**
579
 * get the latest block
580
 *
581
 * @param [cb]          function    callback function to call when request is complete
582
 * @return q.Promise
583
 */
584
APIClient.prototype.blockLatest = function(cb) {
585
    var self = this;
586
587
    return self.client.get("/block/latest", null, cb);
588
};
589
590
/**
591
 * get all transactions for a block (paginated)
592
 *
593
 * @param block         string|int  a block hash or a block height
594
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
595
 * @param [cb]          function    callback function to call when request is complete
596
 * @return q.Promise
597
 */
598
APIClient.prototype.blockTransactions = function(block, params, cb) {
599
    var self = this;
600
601
    if (typeof params === "function") {
602
        cb = params;
603
        params = null;
604
    }
605
606
    return self.client.get("/block/" + block + "/transactions", params, cb);
607
};
608
609
/**
610
 * get a single transaction
611
 *
612
 * @param tx            string      transaction hash
613
 * @param [cb]          function    callback function to call when request is complete
614
 * @return q.Promise
615
 */
616
APIClient.prototype.transaction = function(tx, cb) {
617
    var self = this;
618
619
    return self.client.get("/transaction/" + tx, null, cb);
620
};
621
622
/**
623
 * get a batch of transactions
624
 *
625
 * @param txs           string[]    list of transaction hashes (txId)
626
 * @param [cb]          function    callback function to call when request is complete
627
 * @return q.Promise
628
 */
629
APIClient.prototype.transactions = function(txs, cb) {
630
    var self = this;
631
632
    return self.client.post("/transactions", null, txs, cb, false);
633
};
634
635
/**
636
 * get a paginated list of all webhooks associated with the api user
637
 *
638
 * @param [params]      object      pagination: {page: 1, limit: 20}
639
 * @param [cb]          function    callback function to call when request is complete
640
 * @return q.Promise
641
 */
642
APIClient.prototype.allWebhooks = function(params, cb) {
643
    var self = this;
644
645
    if (typeof params === "function") {
646
        cb = params;
647
        params = null;
648
    }
649
650
    return self.client.get("/webhooks", params, cb);
651
};
652
653
/**
654
 * create a new webhook
655
 *
656
 * @param url           string      the url to receive the webhook events
657
 * @param [identifier]  string      a unique identifier associated with the webhook
658
 * @param [cb]          function    callback function to call when request is complete
659
 * @return q.Promise
660
 */
661
APIClient.prototype.setupWebhook = function(url, identifier, cb) {
662
    var self = this;
663
664
    if (typeof identifier === "function") {
665
        //mimic function overloading
666
        cb = identifier;
667
        identifier = null;
668
    }
669
670
    return self.client.post("/webhook", null, {url: url, identifier: identifier}, cb);
671
};
672
673
/**
674
 * get an existing webhook by it's identifier
675
 *
676
 * @param identifier    string      the unique identifier of the webhook to get
677
 * @param [cb]          function    callback function to call when request is complete
678
 * @return q.Promise
679
 */
680
APIClient.prototype.getWebhook = function(identifier, cb) {
681
    var self = this;
682
683
    return self.client.get("/webhook/" + identifier, null, cb);
684
};
685
686
/**
687
 * update an existing webhook
688
 *
689
 * @param identifier    string      the unique identifier of the webhook
690
 * @param webhookData   object      the data to update: {identifier: newIdentifier, url:newUrl}
691
 * @param [cb]          function    callback function to call when request is complete
692
 * @return q.Promise
693
 */
694
APIClient.prototype.updateWebhook = function(identifier, webhookData, cb) {
695
    var self = this;
696
697
    return self.client.put("/webhook/" + identifier, null, webhookData, cb);
698
};
699
700
/**
701
 * deletes an existing webhook and any event subscriptions associated with it
702
 *
703
 * @param identifier    string      the unique identifier of the webhook
704
 * @param [cb]          function    callback function to call when request is complete
705
 * @return q.Promise
706
 */
707
APIClient.prototype.deleteWebhook = function(identifier, cb) {
708
    var self = this;
709
710
    return self.client.delete("/webhook/" + identifier, null, null, cb);
711
};
712
713
/**
714
 * get a paginated list of all the events a webhook is subscribed to
715
 *
716
 * @param identifier    string      the unique identifier of the webhook
717
 * @param [params]      object      pagination: {page: 1, limit: 20}
718
 * @param [cb]          function    callback function to call when request is complete
719
 * @return q.Promise
720
 */
721
APIClient.prototype.getWebhookEvents = function(identifier, params, cb) {
722
    var self = this;
723
724
    if (typeof params === "function") {
725
        cb = params;
726
        params = null;
727
    }
728
729
    return self.client.get("/webhook/" + identifier + "/events", params, cb);
730
};
731
732
/**
733
 * subscribes a webhook to transaction events for a particular transaction
734
 *
735
 * @param identifier    string      the unique identifier of the webhook
736
 * @param transaction   string      the transaction hash
737
 * @param confirmations integer     the amount of confirmations to send
738
 * @param [cb]          function    callback function to call when request is complete
739
 * @return q.Promise
740
 */
741
APIClient.prototype.subscribeTransaction = function(identifier, transaction, confirmations, cb) {
742
    var self = this;
743
    var postData = {
744
        'event_type': 'transaction',
745
        'transaction': transaction,
746
        'confirmations': confirmations
747
    };
748
749
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
750
};
751
752
/**
753
 * subscribes a webhook to transaction events on a particular address
754
 *
755
 * @param identifier    string      the unique identifier of the webhook
756
 * @param address       string      the address hash
757
 * @param confirmations integer     the amount of confirmations to send
758
 * @param [cb]          function    callback function to call when request is complete
759
 * @return q.Promise
760
 */
761
APIClient.prototype.subscribeAddressTransactions = function(identifier, address, confirmations, cb) {
762
    var self = this;
763
    var postData = {
764
        'event_type': 'address-transactions',
765
        'address': address,
766
        'confirmations': confirmations
767
    };
768
769
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
770
};
771
772
/**
773
 * batch subscribes a webhook to multiple transaction events
774
 *
775
 * @param  identifier   string      the unique identifier of the webhook
776
 * @param  batchData    array       An array of objects containing batch event data:
777
 *                                  {address : 'address', confirmations : 'confirmations']
778
 *                                  where address is the address to subscribe to and confirmations (optional) is the amount of confirmations to send
779
 * @param [cb]          function    callback function to call when request is complete
780
 * @return q.Promise
781
 */
782
APIClient.prototype.batchSubscribeAddressTransactions = function(identifier, batchData, cb) {
783
    var self = this;
784
    batchData.forEach(function(record) {
785
        record.event_type = 'address-transactions';
786
    });
787
788
    return self.client.post("/webhook/" + identifier + "/events/batch", null, batchData, cb);
789
};
790
791
/**
792
 * subscribes a webhook to a new block event
793
 *
794
 * @param identifier    string      the unique identifier of the webhook
795
 * @param [cb]          function    callback function to call when request is complete
796
 * @return q.Promise
797
 */
798
APIClient.prototype.subscribeNewBlocks = function(identifier, cb) {
799
    var self = this;
800
    var postData = {
801
        'event_type': 'block'
802
    };
803
804
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
805
};
806
807
/**
808
 * removes an address transaction event subscription from a webhook
809
 *
810
 * @param identifier    string      the unique identifier of the webhook
811
 * @param address       string      the address hash
812
 * @param [cb]          function    callback function to call when request is complete
813
 * @return q.Promise
814
 */
815
APIClient.prototype.unsubscribeAddressTransactions = function(identifier, address, cb) {
816
    var self = this;
817
818
    return self.client.delete("/webhook/" + identifier + "/address-transactions/" + address, null, null, cb);
819
};
820
821
/**
822
 * removes an transaction event subscription from a webhook
823
 *
824
 * @param identifier    string      the unique identifier of the webhook
825
 * @param transaction   string      the transaction hash
826
 * @param [cb]          function    callback function to call when request is complete
827
 * @return q.Promise
828
 */
829
APIClient.prototype.unsubscribeTransaction = function(identifier, transaction, cb) {
830
    var self = this;
831
832
    return self.client.delete("/webhook/" + identifier + "/transaction/" + transaction, null, null, cb);
833
};
834
835
/**
836
 * removes a block event subscription from a webhook
837
 *
838
 * @param identifier    string      the unique identifier of the webhook
839
 * @param [cb]          function    callback function to call when request is complete
840
 * @return q.Promise
841
 */
842
APIClient.prototype.unsubscribeNewBlocks = function(identifier, cb) {
843
    var self = this;
844
845
    return self.client.delete("/webhook/" + identifier + "/block", null, null, cb);
846
};
847
848
/**
849
 * initialize an existing wallet
850
 *
851
 * Either takes two argument:
852
 * @param options       object      {}
853
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
854
 *
855
 * Or takes three arguments (old, deprecated syntax):
856
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
857
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
858
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 853. The second definition is ignored.
Loading history...
859
 *
860
 * @returns {q.Promise}
861
 */
862
APIClient.prototype.initWallet = function(options, cb) {
863
    var self = this;
864
865
    if (typeof options !== "object") {
866
        // get the old-style arguments
867
        options = {
868
            identifier: arguments[0],
869
            passphrase: arguments[1]
870
        };
871
872
        cb = arguments[2];
873
    }
874
875
    var deferred = q.defer();
876
    deferred.promise.spreadNodeify(cb);
877
878
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
879
880
    var identifier = options.identifier;
881
882
    if (!identifier) {
883
        deferred.reject(new blocktrail.WalletInitError("Identifier is required"));
884
        return deferred.promise;
885
    }
886
887
    deferred.resolve(self.client.get("/wallet/" + identifier, null, true).then(function(result) {
888
        var keyIndex = options.keyIndex || result.key_index;
889
890
        options.walletVersion = result.wallet_version;
891
892
        var backupPublicKey = bitcoin.HDNode.fromBase58(result.backup_public_key[0], network);
893
        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
894
            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
895
        });
896
        var primaryPublicKeys = _.mapValues(result.primary_public_keys, function(primaryPublicKey) {
897
            return bitcoin.HDNode.fromBase58(primaryPublicKey[0], self.network);
898
        });
899
900
        // initialize wallet
901
        var wallet = new Wallet(
902
            self,
903
            identifier,
904
            options.walletVersion,
905
            result.primary_mnemonic,
906
            result.encrypted_primary_seed,
907
            result.encrypted_secret,
908
            primaryPublicKeys,
909
            backupPublicKey,
910
            blocktrailPublicKeys,
911
            keyIndex,
912
            result.chain || 0,
913
            result.segwit || 0,
914
            self.testnet,
915
            result.checksum,
916
            result.upgrade_key_index,
917
            options.bypassNewAddressCheck
918
        );
919
920
        wallet.recoverySecret = result.recovery_secret;
921
922
        if (!options.readOnly) {
923
            return wallet.unlock(options).then(function() {
924
                return wallet;
925
            });
926
        } else {
927
            return wallet;
928
        }
929
    }));
930
931
    return deferred.promise;
932
};
933
934
APIClient.CREATE_WALLET_PROGRESS_START = 0;
935
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET = 4;
936
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY = 5;
937
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY = 6;
938
APIClient.CREATE_WALLET_PROGRESS_PRIMARY = 10;
939
APIClient.CREATE_WALLET_PROGRESS_BACKUP = 20;
940
APIClient.CREATE_WALLET_PROGRESS_SUBMIT = 30;
941
APIClient.CREATE_WALLET_PROGRESS_INIT = 40;
942
APIClient.CREATE_WALLET_PROGRESS_DONE = 100;
943
944
/**
945
 * create a new wallet
946
 *   - will generate a new primary seed and backup seed
947
 *
948
 * Either takes two argument:
949
 * @param options       object      {}
950
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys) // nocommit @TODO
951
 *
952
 * For v1 wallets (explicitly specify options.walletVersion=v1):
953
 * @param options       object      {}
0 ignored issues
show
Documentation introduced by
The parameter options has already been documented on line 949. The second definition is ignored.
Loading history...
954
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 950. The second definition is ignored.
Loading history...
955
 *
956
 * Or takes four arguments (old, deprecated syntax):
957
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
958
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
959
 * @param keyIndex      int         override for the blocktrail cosign key to use (for development purposes)
0 ignored issues
show
Documentation introduced by
The parameter keyIndex does not exist. Did you maybe forget to remove this comment?
Loading history...
960
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 950. The second definition is ignored.
Loading history...
961
 * @returns {q.Promise}
962
 */
963
APIClient.prototype.createNewWallet = function(options, cb) {
964
    /* jshint -W071, -W074 */
965
966
    var self = this;
967
968
    if (typeof options !== "object") {
969
        // get the old-style arguments
970
        var identifier = arguments[0];
971
        var passphrase = arguments[1];
972
        var keyIndex = arguments[2];
973
        cb = arguments[3];
974
975
        // keyIndex is optional
976
        if (typeof keyIndex === "function") {
977
            cb = keyIndex;
978
            keyIndex = null;
979
        }
980
981
        options = {
982
            identifier: identifier,
983
            passphrase: passphrase,
984
            keyIndex: keyIndex
985
        };
986
    }
987
988
    // default to v3
989
    options.walletVersion = options.walletVersion || Wallet.WALLET_VERSION_V3;
990
991
    var deferred = q.defer();
992
    deferred.promise.spreadNodeify(cb);
993
994
    q.nextTick(function() {
995
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_START);
996
997
        options.keyIndex = options.keyIndex || 0;
998
        options.passphrase = options.passphrase || options.password;
999
        delete options.password;
1000
1001
        if (!options.identifier) {
1002
            deferred.reject(new blocktrail.WalletCreateError("Identifier is required"));
1003
            return deferred.promise;
1004
        }
1005
1006
        if (options.walletVersion === Wallet.WALLET_VERSION_V1) {
1007
            self._createNewWalletV1(options)
1008
                .progress(function(p) { deferred.notify(p); })
1009
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1010
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1011
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V2) {
1012
            self._createNewWalletV2(options)
1013
                .progress(function(p) { deferred.notify(p); })
1014
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1015
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1016
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V3) {
1017
            self._createNewWalletV3(options)
1018
                .progress(function(p) { deferred.notify(p); })
1019
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1020
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1021
        } else {
1022
            deferred.reject(new blocktrail.WalletCreateError("Invalid wallet version!"));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1023
        }
1024
    });
1025
1026
    return deferred.promise;
1027
};
1028
1029
APIClient.prototype._createNewWalletV1 = function(options) {
1030
    var self = this;
1031
1032
    var deferred = q.defer();
1033
1034
    q.nextTick(function() {
1035
1036
        if (!options.primaryMnemonic && !options.primarySeed) {
1037
            if (!options.passphrase && !options.password) {
1038
                deferred.reject(new blocktrail.WalletCreateError("Can't generate Primary Mnemonic without a passphrase"));
1039
                return deferred.promise;
1040
            } else {
1041
                options.primaryMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1042
                if (options.storePrimaryMnemonic !== false) {
1043
                    options.storePrimaryMnemonic = true;
1044
                }
1045
            }
1046
        }
1047
1048
        if (!options.backupMnemonic && !options.backupPublicKey) {
1049
            options.backupMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1050
        }
1051
1052
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
1053
1054
        self.resolvePrimaryPrivateKeyFromOptions(options)
1055
            .then(function(options) {
1056
                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
1057
1058
                return self.resolveBackupPublicKeyFromOptions(options)
1059
                    .then(function(options) {
1060
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
1061
1062
                        // create a checksum of our private key which we'll later use to verify we used the right password
1063
                        var checksum = options.primaryPrivateKey.getAddress();
1064
                        var keyIndex = options.keyIndex;
1065
1066
                        var primaryPublicKey = options.primaryPrivateKey.deriveHardened(keyIndex).neutered();
1067
1068
                        // send the public keys to the server to store them
1069
                        //  and the mnemonic, which is safe because it's useless without the password
1070
                        return self.storeNewWalletV1(
1071
                            options.identifier,
1072
                            [primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1073
                            [options.backupPublicKey.toBase58(), "M"],
1074
                            options.storePrimaryMnemonic ? options.primaryMnemonic : false,
1075
                            checksum,
1076
                            keyIndex
1077
                        )
1078
                            .then(function(result) {
1079
                                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1080
1081
                                var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1082
                                    return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1083
                                });
1084
1085
                                var wallet = new Wallet(
1086
                                    self,
1087
                                    options.identifier,
1088
                                    Wallet.WALLET_VERSION_V1,
1089
                                    options.primaryMnemonic,
1090
                                    null,
1091
                                    null,
1092
                                    {keyIndex: primaryPublicKey},
1093
                                    options.backupPublicKey,
1094
                                    blocktrailPublicKeys,
1095
                                    keyIndex,
1096
                                    result.chain || 0,
1097
                                    result.segwit || 0,
1098
                                    self.testnet,
1099
                                    checksum,
1100
                                    result.upgrade_key_index,
1101
                                    options.bypassNewAddressCheck
1102
                                );
1103
1104
                                return wallet.unlock({
1105
                                    walletVersion: Wallet.WALLET_VERSION_V1,
1106
                                    passphrase: options.passphrase,
1107
                                    primarySeed: options.primarySeed,
1108
                                    primaryMnemonic: null // explicit null
1109
                                }).then(function() {
1110
                                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1111
                                    return [
1112
                                        wallet,
1113
                                        {
1114
                                            walletVersion: wallet.walletVersion,
1115
                                            primaryMnemonic: options.primaryMnemonic,
1116
                                            backupMnemonic: options.backupMnemonic,
1117
                                            blocktrailPublicKeys: blocktrailPublicKeys
1118
                                        }
1119
                                    ];
1120
                                });
1121
                            });
1122
                    }
1123
                );
1124
            })
1125
            .then(
1126
            function(r) {
1127
                deferred.resolve(r);
1128
            },
1129
            function(e) {
1130
                deferred.reject(e);
1131
            }
1132
        )
1133
        ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1134
    });
1135
1136
    return deferred.promise;
1137
};
1138
1139 View Code Duplication
APIClient.prototype._createNewWalletV2 = function(options) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1140
    var self = this;
1141
1142
    var deferred = q.defer();
1143
1144
    // avoid modifying passed options
1145
    options = _.merge({}, options);
1146
1147
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
1148
1149
    determineDataStorageV2_3(options)
1150
        .then(function(options) {
1151
            options.passphrase = options.passphrase || options.password;
1152
            delete options.password;
1153
1154
            // avoid deprecated options
1155
            if (options.primaryPrivateKey) {
1156
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1157
            }
1158
1159
            // seed should be provided or generated
1160
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1161
1162
            return options;
1163
        })
1164
        .then(function(options) {
1165
            return produceEncryptedDataV2(options, deferred.notify.bind(deferred));
1166
        })
1167
        .then(function(options) {
1168
            return doRemainingWalletDataV2_3(options, network, deferred.notify.bind(deferred));
1169
        })
1170
        .then(function(options) {
1171
            // create a checksum of our private key which we'll later use to verify we used the right password
1172
            var checksum = options.primaryPrivateKey.getAddress();
1173
            var keyIndex = options.keyIndex;
1174
1175
            // send the public keys and encrypted data to server
1176
            return self.storeNewWalletV2(
1177
                options.identifier,
1178
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1179
                [options.backupPublicKey.toBase58(), "M"],
1180
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1181
                options.storeDataOnServer ? options.encryptedSecret : false,
1182
                options.storeDataOnServer ? options.recoverySecret : false,
1183
                checksum,
1184
                keyIndex,
1185
                options.support_secret || null
1186
            )
1187
                .then(
1188
                function(result) {
1189
                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1190
1191
                    var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1192
                        return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1193
                    });
1194
1195
                    var wallet = new Wallet(
1196
                        self,
1197
                        options.identifier,
1198
                        Wallet.WALLET_VERSION_V2,
1199
                        null,
1200
                        options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1201
                        options.storeDataOnServer ? options.encryptedSecret : null,
1202
                        {keyIndex: options.primaryPublicKey},
1203
                        options.backupPublicKey,
1204
                        blocktrailPublicKeys,
1205
                        keyIndex,
1206
                        result.chain || 0,
1207
                        result.segwit || 0,
1208
                        self.testnet,
1209
                        checksum,
1210
                        result.upgrade_key_index,
1211
                        options.bypassNewAddressCheck
1212
                    );
1213
1214
                    // pass along decrypted data to avoid extra work
1215
                    return wallet.unlock({
1216
                        walletVersion: Wallet.WALLET_VERSION_V2,
1217
                        passphrase: options.passphrase,
1218
                        primarySeed: options.primarySeed,
1219
                        secret: options.secret
1220
                    }).then(function() {
1221
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1222
                        return [
1223
                            wallet,
1224
                            {
1225
                                walletVersion: wallet.walletVersion,
1226
                                encryptedPrimarySeed: options.encryptedPrimarySeed ?
1227
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedPrimarySeed, 'base64', 'hex')) :
1228
                                    null,
1229
                                backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed.toString('hex')) : null,
1230
                                recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1231
                                    bip39.entropyToMnemonic(blocktrail.convert(options.recoveryEncryptedSecret, 'base64', 'hex')) :
1232
                                    null,
1233
                                encryptedSecret: options.encryptedSecret ?
1234
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedSecret, 'base64', 'hex')) :
1235
                                    null,
1236
                                blocktrailPublicKeys: blocktrailPublicKeys
1237
                            }
1238
                        ];
1239
                    });
1240
                }
1241
            );
1242
        })
1243
       .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1244
1245
    return deferred.promise;
1246
};
1247
1248 View Code Duplication
APIClient.prototype._createNewWalletV3 = function(options) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1249
    var self = this;
1250
1251
    var deferred = q.defer();
1252
1253
    // avoid modifying passed options
1254
    options = _.merge({}, options);
1255
1256
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
1257
1258
    determineDataStorageV2_3(options)
1259
        .then(function(options) {
1260
            options.passphrase = options.passphrase || options.password;
1261
            delete options.password;
1262
1263
            // avoid deprecated options
1264
            if (options.primaryPrivateKey) {
1265
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1266
            }
1267
1268
            // seed should be provided or generated
1269
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1270
1271
            return options;
1272
        })
1273
        .then(function(options) {
1274
            return self.produceEncryptedDataV3(options, deferred.notify.bind(deferred));
1275
        })
1276
        .then(function(options) {
1277
            return doRemainingWalletDataV2_3(options, network, deferred.notify.bind(deferred));
1278
        })
1279
        .then(function(options) {
1280
1281
            // create a checksum of our private key which we'll later use to verify we used the right password
1282
            var checksum = options.primaryPrivateKey.getAddress();
1283
            var keyIndex = options.keyIndex;
1284
1285
            // send the public keys and encrypted data to server
1286
            return self.storeNewWalletV3(
1287
                options.identifier,
1288
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1289
                [options.backupPublicKey.toBase58(), "M"],
1290
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1291
                options.storeDataOnServer ? options.encryptedSecret : false,
1292
                options.storeDataOnServer ? options.recoverySecret : false,
1293
                checksum,
1294
                keyIndex,
1295
                options.support_secret || null
1296
            )
1297
                .then(
1298
                    // result, deferred, self(apiclient)
1299
                    function(result) {
1300
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1301
1302
                        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1303
                            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1304
                        });
1305
1306
                        var wallet = new Wallet(
1307
                            self,
1308
                            options.identifier,
1309
                            Wallet.WALLET_VERSION_V3,
1310
                            null,
1311
                            options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1312
                            options.storeDataOnServer ? options.encryptedSecret : null,
1313
                            {keyIndex: options.primaryPublicKey},
1314
                            options.backupPublicKey,
1315
                            blocktrailPublicKeys,
1316
                            keyIndex,
1317
                            result.chain || 0,
1318
                            result.segwit || 0,
1319
                            self.testnet,
1320
                            checksum,
1321
                            result.upgrade_key_index,
1322
                            options.bypassNewAddressCheck
1323
                        );
1324
1325
                        // pass along decrypted data to avoid extra work
1326
                        return wallet.unlock({
1327
                            walletVersion: Wallet.WALLET_VERSION_V3,
1328
                            passphrase: options.passphrase,
1329
                            primarySeed: options.primarySeed,
1330
                            secret: options.secret
1331
                        }).then(function() {
1332
                            deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1333
                            return [
1334
                                wallet,
1335
                                {
1336
                                    walletVersion: wallet.walletVersion,
1337
                                    encryptedPrimarySeed: options.encryptedPrimarySeed ? EncryptionMnemonic.encode(options.encryptedPrimarySeed) : null,
1338
                                    backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed) : null,
1339
                                    recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1340
                                        EncryptionMnemonic.encode(options.recoveryEncryptedSecret) : null,
1341
                                    encryptedSecret: options.encryptedSecret ? EncryptionMnemonic.encode(options.encryptedSecret) : null,
1342
                                    blocktrailPublicKeys: blocktrailPublicKeys
1343
                                }
1344
                            ];
1345
                        });
1346
                    }
1347
                );
1348
        })
1349
        .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1350
1351
    return deferred.promise;
1352
};
1353
1354
function verifyPublicBip32Key(bip32Key, network) {
1355
    var hk = bitcoin.HDNode.fromBase58(bip32Key[0], network);
1356
    if (typeof hk.keyPair.d !== "undefined") {
1357
        throw new Error('BIP32Key contained private key material - abort');
1358
    }
1359
1360
    if (bip32Key[1].slice(0, 1) !== "M") {
1361
        throw new Error("BIP32Key contained non-public path - abort");
1362
    }
1363
}
1364
1365
function verifyPublicOnly(walletData, network) {
1366
    verifyPublicBip32Key(walletData.primary_public_key, network);
1367
    verifyPublicBip32Key(walletData.backup_public_key, network);
1368
}
1369
1370
/**
1371
 * create wallet using the API
1372
 *
1373
 * @param identifier            string      the wallet identifier to create
1374
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1375
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1376
 * @param primaryMnemonic       string      mnemonic to store
1377
 * @param checksum              string      checksum to store
1378
 * @param keyIndex              int         keyIndex that was used to create wallet
1379
 * @param [cb]                  function    callback(err, result)
1380
 * @returns {q.Promise}
1381
 */
1382
APIClient.prototype.storeNewWalletV1 = function(identifier, primaryPublicKey, backupPublicKey, primaryMnemonic, checksum, keyIndex, cb) {
1383
    var self = this;
1384
1385
    var postData = {
1386
        identifier: identifier,
1387
        wallet_version: Wallet.WALLET_VERSION_V1,
1388
        primary_public_key: primaryPublicKey,
1389
        backup_public_key: backupPublicKey,
1390
        primary_mnemonic: primaryMnemonic,
1391
        checksum: checksum,
1392
        key_index: keyIndex
1393
    };
1394
1395
    verifyPublicOnly(postData, self.network);
1396
1397
    return self.client.post("/wallet", null, postData, cb);
1398
};
1399
1400
/**
1401
 * create wallet using the API
1402
 *
1403
 * @param identifier            string      the wallet identifier to create
1404
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1405
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1406
 * @param encryptedPrimarySeed  string      openssl format
1407
 * @param encryptedSecret       string      openssl format
1408
 * @param recoverySecret        string      openssl format
1409
 * @param checksum              string      checksum to store
1410
 * @param keyIndex              int         keyIndex that was used to create wallet
1411
 * @param supportSecret         string
1412
 * @param [cb]                  function    callback(err, result)
1413
 * @returns {q.Promise}
1414
 */
1415
APIClient.prototype.storeNewWalletV2 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1416
                                                recoverySecret, checksum, keyIndex, supportSecret, cb) {
1417
    var self = this;
1418
1419
    var postData = {
1420
        identifier: identifier,
1421
        wallet_version: Wallet.WALLET_VERSION_V2,
1422
        primary_public_key: primaryPublicKey,
1423
        backup_public_key: backupPublicKey,
1424
        encrypted_primary_seed: encryptedPrimarySeed,
1425
        encrypted_secret: encryptedSecret,
1426
        recovery_secret: recoverySecret,
1427
        checksum: checksum,
1428
        key_index: keyIndex,
1429
        support_secret: supportSecret || null
1430
    };
1431
1432
    verifyPublicOnly(postData, self.network);
1433
1434
    return self.client.post("/wallet", null, postData, cb);
1435
};
1436
1437
/**
1438
 * create wallet using the API
1439
 *
1440
 * @param identifier            string      the wallet identifier to create
1441
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1442
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1443
 * @param encryptedPrimarySeed  Buffer      buffer of ciphertext
1444
 * @param encryptedSecret       Buffer      buffer of ciphertext
1445
 * @param recoverySecret        Buffer      buffer of recovery secret
1446
 * @param checksum              string      checksum to store
1447
 * @param keyIndex              int         keyIndex that was used to create wallet
1448
 * @param supportSecret         string
1449
 * @param [cb]                  function    callback(err, result)
1450
 * @returns {q.Promise}
1451
 */
1452
APIClient.prototype.storeNewWalletV3 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1453
                                                recoverySecret, checksum, keyIndex, supportSecret, cb) {
1454
    var self = this;
1455
1456
    var postData = {
1457
        identifier: identifier,
1458
        wallet_version: Wallet.WALLET_VERSION_V3,
1459
        primary_public_key: primaryPublicKey,
1460
        backup_public_key: backupPublicKey,
1461
        encrypted_primary_seed: encryptedPrimarySeed.toString('base64'),
1462
        encrypted_secret: encryptedSecret.toString('base64'),
1463
        recovery_secret: recoverySecret.toString('hex'),
1464
        checksum: checksum,
1465
        key_index: keyIndex,
1466
        support_secret: supportSecret || null
1467
    };
1468
1469
    verifyPublicOnly(postData, self.network);
1470
1471
    return self.client.post("/wallet", null, postData, cb);
1472
};
1473
1474
/**
1475
 * create wallet using the API
1476
 *
1477
 * @param identifier            string      the wallet identifier to create
1478
 * @param postData              object
1479
 * @param [cb]                  function    callback(err, result)
1480
 * @returns {q.Promise}
1481
 */
1482
APIClient.prototype.updateWallet = function(identifier, postData, cb) {
1483
    var self = this;
1484
1485
    return self.client.post("/wallet/" + identifier, null, postData, cb);
1486
};
1487
1488
/**
1489
 * upgrade wallet to use a new account number
1490
 *  the account number specifies which blocktrail cosigning key is used
1491
 *
1492
 * @param identifier            string      the wallet identifier
1493
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1494
 * @param keyIndex              int         keyIndex that was used to create wallet
1495
 * @param [cb]                  function    callback(err, result)
1496
 * @returns {q.Promise}
1497
 */
1498
APIClient.prototype.upgradeKeyIndex = function(identifier, keyIndex, primaryPublicKey, cb) {
1499
    var self = this;
1500
1501
    return self.client.post("/wallet/" + identifier + "/upgrade", null, {
1502
        key_index: keyIndex,
1503
        primary_public_key: primaryPublicKey
1504
    }, cb);
1505
};
1506
1507
/**
1508
 * get the balance for the wallet
1509
 *
1510
 * @param identifier            string      the wallet identifier
1511
 * @param [cb]                  function    callback(err, result)
1512
 * @returns {q.Promise}
1513
 */
1514
APIClient.prototype.getWalletBalance = function(identifier, cb) {
1515
    var self = this;
1516
1517
    return self.client.get("/wallet/" + identifier + "/balance", null, true, cb);
1518
};
1519
1520
/**
1521
 * do HD wallet discovery for the wallet
1522
 *
1523
 * @param identifier            string      the wallet identifier
1524
 * @param [cb]                  function    callback(err, result)
1525
 * @returns {q.Promise}
1526
 */
1527
APIClient.prototype.doWalletDiscovery = function(identifier, gap, cb) {
1528
    var self = this;
1529
1530
    return self.client.get("/wallet/" + identifier + "/discovery", {gap: gap}, true, cb);
1531
};
1532
1533
1534
/**
1535
 * get a new derivation number for specified parent path
1536
 *  eg; m/44'/1'/0/0 results in m/44'/1'/0/0/0 and next time in m/44'/1'/0/0/1 and next time in m/44'/1'/0/0/2
1537
 *
1538
 * @param identifier            string      the wallet identifier
1539
 * @param path                  string      the parent path for which to get a new derivation,
1540
 *                                           can be suffixed with /* to make it clear on which level the derivations hould be
1541
 * @param [cb]                  function    callback(err, result)
1542
 * @returns {q.Promise}
1543
 */
1544
APIClient.prototype.getNewDerivation = function(identifier, path, cb) {
1545
    var self = this;
1546
1547
    return self.client.post("/wallet/" + identifier + "/path", null, {path: path}, cb);
1548
};
1549
1550
1551
/**
1552
 * delete the wallet
1553
 *  the checksum address and a signature to verify you ownership of the key of that checksum address
1554
 *  is required to be able to delete a wallet
1555
 *
1556
 * @param identifier            string      the wallet identifier
1557
 * @param checksumAddress       string      the address for your master private key (and the checksum used when creating the wallet)
1558
 * @param checksumSignature     string      a signature of the checksum address as message signed by the private key matching that address
1559
 * @param [force]               bool        ignore warnings (such as a non-zero balance)
1560
 * @param [cb]                  function    callback(err, result)
1561
 * @returns {q.Promise}
1562
 */
1563
APIClient.prototype.deleteWallet = function(identifier, checksumAddress, checksumSignature, force, cb) {
1564
    var self = this;
1565
1566
    if (typeof force === "function") {
1567
        cb = force;
1568
        force = false;
1569
    }
1570
1571
    return self.client.delete("/wallet/" + identifier, {force: force}, {
1572
        checksum: checksumAddress,
1573
        signature: checksumSignature
1574
    }, cb);
1575
};
1576
1577
/**
1578
 * use the API to get the best inputs to use based on the outputs
1579
 *
1580
 * the return array has the following format:
1581
 * [
1582
 *  "utxos" => [
1583
 *      [
1584
 *          "hash" => "<txHash>",
1585
 *          "idx" => "<index of the output of that <txHash>",
1586
 *          "scriptpubkey_hex" => "<scriptPubKey-hex>",
1587
 *          "value" => 32746327,
1588
 *          "address" => "1address",
1589
 *          "path" => "m/44'/1'/0'/0/13",
1590
 *          "redeem_script" => "<redeemScript-hex>",
1591
 *      ],
1592
 *  ],
1593
 *  "fee"   => 10000,
1594
 *  "change"=> 1010109201,
1595
 * ]
1596
 *
1597
 * @param identifier        string      the wallet identifier
1598
 * @param pay               array       {'address': (int)value}     coins to send
1599
 * @param lockUTXO          bool        lock UTXOs for a few seconds to allow for transaction to be created
1600
 * @param allowZeroConf     bool        allow zero confirmation unspent outputs to be used in coin selection
1601
 * @param feeStrategy       string      defaults to
1602
 * @param options
1603
 * @param [cb]              function    callback(err, utxos, fee, change)
1604
 * @returns {q.Promise}
1605
 */
1606
APIClient.prototype.coinSelection = function(identifier, pay, lockUTXO, allowZeroConf, feeStrategy, options, cb) {
1607
    var self = this;
1608
1609
    if (typeof feeStrategy === "function") {
1610
        cb = feeStrategy;
1611
        feeStrategy = null;
1612
        options = {};
1613
    } else if (typeof options === "function") {
1614
        cb = options;
1615
        options = {};
1616
    }
1617
1618
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1619
    options = options || {};
1620
1621
    var deferred = q.defer();
1622
    deferred.promise.spreadNodeify(cb);
1623
1624
    var params = {
1625
        lock: lockUTXO,
1626
        zeroconf: allowZeroConf ? 1 : 0,
1627
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1628
        fee_strategy: feeStrategy
1629
    };
1630
1631
    if (options.forcefee) {
1632
        params['forcefee'] = options.forcefee;
1633
    }
1634
1635
    deferred.resolve(
1636
        self.client.post("/wallet/" + identifier + "/coin-selection", params, pay).then(
1637
            function(result) {
1638
                return [result.utxos, result.fee, result.change, result];
1639
            },
1640
            function(err) {
1641
                if (err.message.match(/too low to pay the fee/)) {
1642
                    throw blocktrail.WalletFeeError(err);
1643
                }
1644
1645
                throw err;
1646
            }
1647
        )
1648
    );
1649
1650
    return deferred.promise;
1651
};
1652
1653
/**
1654
 * @param [cb]              function    callback(err, utxos, fee, change)
1655
 * @returns {q.Promise}
1656
 */
1657
APIClient.prototype.feePerKB = function(cb) {
1658
    var self = this;
1659
1660
    var deferred = q.defer();
1661
    deferred.promise.spreadNodeify(cb);
1662
1663
    deferred.resolve(self.client.get("/fee-per-kb"));
1664
1665
    return deferred.promise;
1666
};
1667
1668
/**
1669
 * send the transaction using the API
1670
 *
1671
 * @param identifier        string      the wallet identifier
1672
 * @param txHex             string      partially signed transaction as hex string
1673
 * @param paths             array       list of paths used in inputs which should be cosigned by the API
1674
 * @param checkFee          bool        when TRUE the API will verify if the fee is 100% correct and otherwise throw an exception
1675
 * @param [twoFactorToken]  string      2FA token
1676
 * @param [prioboost]       bool
1677
 * @param [cb]              function    callback(err, txHash)
1678
 * @returns {q.Promise}
1679
 */
1680
APIClient.prototype.sendTransaction = function(identifier, txHex, paths, checkFee, twoFactorToken, prioboost, cb) {
1681
    var self = this;
1682
1683
    if (typeof twoFactorToken === "function") {
1684
        cb = twoFactorToken;
1685
        twoFactorToken = null;
1686
        prioboost = false;
1687
    } else if (typeof prioboost === "function") {
1688
        cb = prioboost;
1689
        prioboost = false;
1690
    }
1691
1692
    var data = {
1693
        paths: paths,
1694
        two_factor_token: twoFactorToken
1695
    };
1696
    if (typeof txHex === "string") {
1697
        data.raw_transaction = txHex;
1698
    } else if (typeof txHex === "object") {
1699
        Object.keys(txHex).map(function(key) {
1700
            data[key] = txHex[key];
1701
        });
1702
    }
1703
1704
    return self.client.post(
1705
        "/wallet/" + identifier + "/send",
1706
        {
1707
            check_fee: checkFee ? 1 : 0,
1708
            prioboost: prioboost ? 1 : 0
1709
        },
1710
        data,
1711
        cb
1712
    );
1713
};
1714
1715
/**
1716
 * setup a webhook for this wallet
1717
 *
1718
 * @param identifier        string      the wallet identifier
1719
 * @param webhookIdentifier string      identifier for the webhook
1720
 * @param url               string      URL to receive webhook events
1721
 * @param [cb]              function    callback(err, webhook)
1722
 * @returns {q.Promise}
1723
 */
1724
APIClient.prototype.setupWalletWebhook = function(identifier, webhookIdentifier, url, cb) {
1725
    var self = this;
1726
1727
    return self.client.post("/wallet/" + identifier + "/webhook", null, {url: url, identifier: webhookIdentifier}, cb);
1728
};
1729
1730
/**
1731
 * delete a webhook that was created for this wallet
1732
 *
1733
 * @param identifier        string      the wallet identifier
1734
 * @param webhookIdentifier string      identifier for the webhook
1735
 * @param [cb]              function    callback(err, success)
1736
 * @returns {q.Promise}
1737
 */
1738
APIClient.prototype.deleteWalletWebhook = function(identifier, webhookIdentifier, cb) {
1739
    var self = this;
1740
1741
    return self.client.delete("/wallet/" + identifier + "/webhook/" + webhookIdentifier, null, null, cb);
1742
};
1743
1744
/**
1745
 * get all transactions for an wallet (paginated)
1746
 *
1747
 * @param identifier    string      wallet identifier
1748
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1749
 * @param [cb]          function    callback function to call when request is complete
1750
 * @return q.Promise
1751
 */
1752
APIClient.prototype.walletTransactions = function(identifier, params, cb) {
1753
    var self = this;
1754
1755
    if (typeof params === "function") {
1756
        cb = params;
1757
        params = null;
1758
    }
1759
1760
    return self.client.get("/wallet/" + identifier + "/transactions", params, true, cb);
1761
};
1762
1763
/**
1764
 * get all addresses for an wallet (paginated)
1765
 *
1766
 * @param identifier    string      wallet identifier
1767
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1768
 * @param [cb]          function    callback function to call when request is complete
1769
 * @return q.Promise
1770
 */
1771
APIClient.prototype.walletAddresses = function(identifier, params, cb) {
1772
    var self = this;
1773
1774
    if (typeof params === "function") {
1775
        cb = params;
1776
        params = null;
1777
    }
1778
1779
    return self.client.get("/wallet/" + identifier + "/addresses", params, true, cb);
1780
};
1781
1782
/**
1783
 * @param identifier    string      wallet identifier
1784
 * @param address       string      the address to label
1785
 * @param label         string      the label
1786
 * @param [cb]          function    callback(err, res)
1787
 * @return q.Promise
1788
 */
1789
APIClient.prototype.labelWalletAddress = function(identifier, address, label, cb) {
1790
    var self = this;
1791
1792
    return self.client.post("/wallet/" + identifier + "/address/" + address + "/label", null, {label: label}, cb);
1793
};
1794
1795
APIClient.prototype.walletMaxSpendable = function(identifier, allowZeroConf, feeStrategy, options, cb) {
1796
    var self = this;
1797
1798
    if (typeof feeStrategy === "function") {
1799
        cb = feeStrategy;
1800
        feeStrategy = null;
1801
    } else if (typeof options === "function") {
1802
        cb = options;
1803
        options = {};
1804
    }
1805
1806
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1807
    options = options || {};
1808
1809
    var params = {
1810
        outputs: options.outputs ? options.outputs : 1,
1811
        zeroconf: allowZeroConf ? 1 : 0,
1812
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1813
        fee_strategy: feeStrategy
1814
    };
1815
1816
    if (options.forcefee) {
1817
        params['forcefee'] = options.forcefee;
1818
    }
1819
1820
    return self.client.get("/wallet/" + identifier + "/max-spendable", params, true, cb);
1821
};
1822
1823
/**
1824
 * get all UTXOs for an wallet (paginated)
1825
 *
1826
 * @param identifier    string      wallet identifier
1827
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1828
 * @param [cb]          function    callback function to call when request is complete
1829
 * @return q.Promise
1830
 */
1831
APIClient.prototype.walletUTXOs = function(identifier, params, cb) {
1832
    var self = this;
1833
1834
    if (typeof params === "function") {
1835
        cb = params;
1836
        params = null;
1837
    }
1838
1839
    return self.client.get("/wallet/" + identifier + "/utxos", params, true, cb);
1840
};
1841
1842
/**
1843
 * get a paginated list of all wallets associated with the api user
1844
 *
1845
 * @param [params]      object      pagination: {page: 1, limit: 20}
1846
 * @param [cb]          function    callback function to call when request is complete
1847
 * @return q.Promise
1848
 */
1849
APIClient.prototype.allWallets = function(params, cb) {
1850
    var self = this;
1851
1852
    if (typeof params === "function") {
1853
        cb = params;
1854
        params = null;
1855
    }
1856
1857
    return self.client.get("/wallets", params, true, cb);
1858
};
1859
1860
/**
1861
 * verify a message signed bitcoin-core style
1862
 *
1863
 * @param message        string
1864
 * @param address        string
1865
 * @param signature      string
1866
 * @param [cb]          function    callback function to call when request is complete
1867
 * @return q.Promise
1868
 */
1869
APIClient.prototype.verifyMessage = function(message, address, signature, cb) {
1870
    var self = this;
1871
1872
    // we could also use the API instead of the using bitcoinjs-lib to verify
1873
    // return self.client.post("/verify_message", null, {message: message, address: address, signature: signature}, cb);
1874
1875
    var deferred = q.defer();
1876
    deferred.promise.nodeify(cb);
1877
    try {
1878
        var result = bitcoinMessage.verify(address, self.network.messagePrefix, message, new Buffer(signature, 'base64'));
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1879
        deferred.resolve(result);
1880
    } catch (e) {
1881
        deferred.reject(e);
1882
    }
1883
1884
    return deferred.promise;
1885
};
1886
1887
/**
1888
 * max is 0.001
1889
 * testnet only
1890
 *
1891
 * @param address
1892
 * @param amount
1893
 * @param cb
1894
 */
1895
APIClient.prototype.faucetWithdrawl = function(address, amount, cb) {
1896
    var self = this;
1897
1898
    return self.client.post("/faucet/withdrawl", null, {address: address, amount: amount}, cb);
1899
};
1900
1901
/**
1902
 * send a raw transaction
1903
 *
1904
 * @param rawTransaction    string      raw transaction as HEX
1905
 * @param [cb]              function    callback function to call when request is complete
1906
 * @return q.Promise
1907
 */
1908
APIClient.prototype.sendRawTransaction = function(rawTransaction, cb) {
1909
    var self = this;
1910
1911
    return self.client.post("/send-raw-tx", null, rawTransaction, cb);
1912
};
1913
1914
/**
1915
 * get the current price index
1916
 *
1917
 * @param [cb]          function    callback({'USD': 287.30})
1918
 * @return q.Promise
1919
 */
1920
APIClient.prototype.price = function(cb) {
1921
    var self = this;
1922
1923
    return self.client.get("/price", null, false, cb);
1924
};
1925
1926
module.exports = APIClient;
1927